﻿/* 
* Copyright (c) 2010, The University of Texas at Austin
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, 
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, 
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, 
* this list of conditions and the following disclaimer in the documentation and/or other materials 
* provided with the distribution.
*
* Neither the name of The University of Texas at Austin nor the names of its contributors may be 
* used to endorse or promote products derived from this software without specific prior written 
* permission.
* 
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS 
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Diagnostics;
using System.Globalization;
using System.Windows;
using System.Windows.Media;

namespace Bio.Views.Structure.Editor.Internal.Rendering.Visuals
{
    public class NucleotideDrawingVisual : DrawingVisual
    {
        public Rect Measure()
        {
            Typeface = new Typeface(Container.NucleotideFontFamily, Container.NucleotideFontStyle.Value, Container.NucleotideFontWeight.Value, FontStretches.Normal);
            FormattedText renderer = new FormattedText("W", CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, Typeface, Container.NucleotideFontSize.Value, Container.NucleotideFill);
            return new Rect(Container.Location.X - Math.Round(renderer.Width / 2.0),
                            Container.Location.Y - Math.Round(renderer.Height / 2.0),
                            renderer.Width,
                            renderer.Height);
        }

        public void Render()
        {
            using (var drawingContext = RenderOpen())
            {
                if (Container.IsComponentVisible)
                {
                    if (Container.IsSelected)
                    {
                        drawingContext.DrawRectangle(TRANSPARENT_BRUSH, new Pen(Container.SelectionFill, 1.0), Container.Bounds);
                    }
                    else if (Container.IsContextuallySelected)
                    {
                        drawingContext.DrawRectangle(TRANSPARENT_BRUSH, new Pen(Container.ContextualSelectionFill, 0.25) { LineJoin = PenLineJoin.Round, EndLineCap = PenLineCap.Round }, Container.Bounds);                    
                    }
                    else
                    {
                        drawingContext.DrawRectangle(TRANSPARENT_BRUSH, null, Container.Bounds);
                    }

                    if (Container.ShowNucleotideConnectors)
                    {
                        drawingContext.DrawLine(new Pen(CONNECTOR_BRUSH, 0.75), Container.NucleotideConnector.PreviousNucleotide, Container.NucleotideConnector.NucleotideCenter);
                        drawingContext.DrawLine(new Pen(CONNECTOR_BRUSH, 0.75), Container.NucleotideConnector.NucleotideCenter, Container.NucleotideConnector.NextNucleotide);
                    }

                    if (Container.NucleotideRenderingStyle == NucleotideRenderingMode.Text)
                    {
                        FormattedText renderer = null;
                        if(!Container.IsNucleotideSubstituted.Value)
                            renderer = new FormattedText(Char.ToString(Container.Data.Value), CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, Typeface, Container.NucleotideFontSize.Value, Container.NucleotideFill);
                        else
                            renderer = new FormattedText(Char.ToString(Container.NucleotideSubstitution.Value), CultureInfo.GetCultureInfo("en-us"), FlowDirection.LeftToRight, Typeface, Container.NucleotideFontSize.Value, Container.NucleotideFill);
                        double xOffset = (renderer.Width > Container.Bounds.Width) ? 0.0 : Math.Round((Container.Bounds.Width - renderer.Width) / 2.0);
                        double yOffset = (renderer.Height > Container.Bounds.Height) ? 0.0 : Math.Round((Container.Bounds.Height - renderer.Height) / 2.0);
                        drawingContext.DrawText(renderer, new Point(Container.Bounds.TopLeft.X + xOffset, Container.Bounds.TopLeft.Y + yOffset));
                    }
                    else if (Container.NucleotideRenderingStyle == NucleotideRenderingMode.Circle)
                    {
                        double edgeThickness = 0.0;
                        double maxRadius = 0.0;

                        if (Container.NucleotideCircleEdgeThickness.Value <= 0.0)
                            edgeThickness = 0.25; //We need to stub in some stroke thickness to give rendering symmetry.
                        else
                            edgeThickness = Container.NucleotideCircleEdgeThickness.Value;
                                                
                        if (Container.Bounds.Height == Container.Bounds.Width ||
                            Container.Bounds.Height < Container.Bounds.Width)
                        {
                            maxRadius = Math.Round((0.80 * Container.Bounds.Height) / 2.0);
                        }
                        else
                        {
                            maxRadius = Math.Round((0.80 * Container.Bounds.Width) / 2.0);
                        }

                        double desiredRadius = ((Container.NucleotideCircleDiameter.Value / 2.0) + edgeThickness); 
                        if (desiredRadius <= 0.0 || desiredRadius > maxRadius)
                            desiredRadius = maxRadius;

                        //Center the rendering within the bounds.
                        Point center = new Point(Container.Bounds.TopLeft.X + (Container.Bounds.Width / 2.0), Container.Bounds.TopLeft.Y + (Container.Bounds.Height / 2.0));

                        Pen renderingPen = null;
                        Brush fillBrush = null;
                        if (!Container.NucleotideCircleIsFilled.Value)
                        {
                            renderingPen = new Pen(Container.NucleotideCircleStroke, edgeThickness);
                        }
                        else 
                        {
                            fillBrush = Container.NucleotideCircleFill;
                            renderingPen = (Container.NucleotideCircleStroke != null && Container.NucleotideCircleEdgeThickness > 0.0) ? new Pen(Container.NucleotideCircleStroke, edgeThickness) : new Pen(fillBrush, edgeThickness);
                        }

                        drawingContext.DrawEllipse(fillBrush, renderingPen, center, desiredRadius, desiredRadius);
                    }
                    else //Has to be Rectangle rendering mode.
                    {
                        double ntRectWidth = Container.Bounds.Width * 0.80;
                        double ntRectHeight = Container.Bounds.Height * 0.80;
                        double ntRectXOffset = Math.Round((Container.Bounds.Width - ntRectWidth) / 2.0);
                        double ntRectYOffset = Math.Round((Container.Bounds.Height - ntRectHeight) / 2.0);
                        Rect ntRect = new Rect(Container.Bounds.TopLeft.X + ntRectXOffset, Container.Bounds.TopLeft.Y + ntRectYOffset,
                            ntRectWidth, ntRectHeight);
                        drawingContext.DrawRectangle(Container.NucleotideFill, null, ntRect);
                    }
                }
                else
                {
                    //If were not visible, we draw an invisble rectangle to be included in the bounds calculations.
                    drawingContext.DrawRectangle(TRANSPARENT_BRUSH, null, Container.Bounds);
                }
            }   
        }

        static NucleotideDrawingVisual()
        {
            TRANSPARENT_BRUSH.Freeze();
            CONNECTOR_BRUSH.Freeze();
        }

        public NucleotideDrawingVisual(NucleotideStructureElementVisualComponent container)
        {
            Debug.Assert(container != null);
            Container = container;
        }

        private NucleotideStructureElementVisualComponent Container { get; set; }
        private Typeface Typeface { get; set; }
        private static readonly Brush TRANSPARENT_BRUSH = new SolidColorBrush(Colors.Transparent);
        private static readonly Brush CONNECTOR_BRUSH = new SolidColorBrush(Colors.LightGray);
    }
}
